/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2019/6/1 0:28
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.aurora.core.init;

import com.hyacinth.aurora.core.exception.SingleClassInstanceException;
import com.hyacinth.aurora.utils.ClassUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * 抽象单实例基类
 * 单例类均可继承该类
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
public class AbstractSingleton {

    /**
     * 单实例缓存
     */
    private static final Map<String, AbstractSingleton> registryMap = new HashMap<>();

    AbstractSingleton() throws SingleClassInstanceException {
        String clazzName = this.getClass().getName();
        if (registryMap.containsKey(clazzName)){
            throw new SingleClassInstanceException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
        } else {
            synchronized(registryMap){
                if (registryMap.containsKey(clazzName)){
                    throw new SingleClassInstanceException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
                } else {
                    registryMap.put(clazzName, this);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws SingleClassInstanceException{
        String clazzName = clazz.getName();
        if(!registryMap.containsKey(clazzName)){
            synchronized(registryMap){
                if(!registryMap.containsKey(clazzName)){
                    try {
                        Constructor<?> constructor = clazz.getDeclaredConstructor();
                        constructor.setAccessible(true);
                        return (T) constructor.newInstance();
                    } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                        throw new SingleClassInstanceException("单例类实例化异常", e);
                    }
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    public static AbstractSingleton getInstance(final String clazzName) throws SingleClassInstanceException {
        if(!registryMap.containsKey(clazzName)){
            synchronized(registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    try {
                        Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
                        // 设置可访问权限
                        ClassUtil.setAccessible(clazz, true);
                        return clazz.newInstance();
                    } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException e) {
                        throw new SingleClassInstanceException("单例类实例化异常", e);
                    }
                }
            }
        }
        return registryMap.get(clazzName);
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
            throws SingleClassInstanceException {
        String clazzName = clazz.getName();
        if(!registryMap.containsKey(clazzName)){
            synchronized(registryMap){
                if(!registryMap.containsKey(clazzName)){
                    try {
                        // 设置可访问权限
                        ClassUtil.setAccessible(clazz, true);
                        Constructor<T> constructor = clazz.getConstructor(parameterTypes);
                        return constructor.newInstance(initargs);
                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                        throw new SingleClassInstanceException("单例类实例化异常", e);
                    }
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

}
